/*
This file is part of IRCaBot.
IRCaBot is IRC logger with features.
Source code: https://notabug.org/acetone/ircabot.
Copyright (C) acetone, 2023.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

#pragma once

#include "currenttime.h"

#include <QMutex>
#include <QString>
#include <QDateTime>
#include <QSharedPointer>

constexpr const char CACHE_ID_DATE_FORMAT[] = "yyyy-MM-dd"; // + _NUMBER

class CachedMessage;

class IRCLogger
{
public:
    class Message
    {
        friend IRCLogger;
    public:
        Message(){}
        Message(const QString& sender, const QString& text);

        QString bridge() const { return m_bridge; }
        QString text() const { return m_text; }
        QString sender() const { return m_sender; }
        quint64 number() const { return m_number; }

    private:
        void initFromRaw(const QString& raw, quint64 number);

        QString m_sender;
        QString m_bridge;
        QString m_text;
        quint64 m_number = 0;
    };


    class Cache // for real-time reading mode
    {
    public:
        class CacheId
        {
            friend bool operator>(const CacheId& lhs, const CacheId& rhs)
            {
                if (lhs.m_date > rhs.m_date) return true;
                if (lhs.m_date < rhs.m_date) return false;
                return lhs.m_number > rhs.m_number;
            }
            friend bool operator<(const CacheId& lhs, const CacheId& rhs)
            {
                if (lhs.m_date < rhs.m_date) return true;
                if (lhs.m_date > rhs.m_date) return false;
                return lhs.m_number < rhs.m_number;
            }
            friend bool operator==(const CacheId& lhs, const CacheId& rhs)
            {
                return lhs.m_date == rhs.m_date and lhs.m_number == rhs.m_number;
            }
            friend bool operator<=(const CacheId& lhs, const CacheId& rhs)
            {
                return lhs < rhs or lhs == rhs;
            }

        public:
            CacheId() : m_date( currentDateTime() ), m_number(0) {}
            CacheId(const QString& raw);

        private:
            QDateTime m_date;
            quint64 m_number;
        };


        class CachedMessage : public Message
        {
        public:
            CachedMessage(const Message& msg) : Message(msg) {}
            CacheId id() const { return m_id; }

        private:
            CacheId m_id;
        };


        Cache(){}
        CacheId back() const;
        QList<QSharedPointer<Message>> messagesAfter(const CacheId& id);
        void add(const Message& msg);

    private:
        mutable QMutex m_mtx;
        QList<QSharedPointer<CachedMessage>> m_data;
    };

    using Month = QPair<int8_t, QList<int8_t>>; // Month number, days list
    using Year = QPair<int16_t, QList<Month>>; // Year number, months list

    IRCLogger(const QString& network, const QString& channel);
    QString network() const { return m_network; }
    QString channel() const { return m_channel; }
    QList<Year> dateIndex() const;

    void addMessage(const QString& sender, const QString& message);
    QList<QSharedPointer<Message>> getMessages(const QDateTime& date, const quint64* beginId = nullptr, const quint64* endId = nullptr);

private:
    void doDateIndex();
    QString dateDir(const QDateTime& date);
    QString datePath(const QDateTime& date);

    mutable QMutex m_dateIndexMtx;
    QList<Year> m_dateIndex;
    Cache m_cache;
    QMutex m_fileIOmtx;
    const QString m_network;
    const QString m_channel;
    const QString m_rootDir;
};
